﻿#include "OiPCH.hpp"
#include <QSettings>
#include "Camera/OiLT360CameraDriver.hpp"
#include "Math/OiOptimize.hpp"

namespace Oi
{
    namespace PLScannerCore
    {
        QString getAppDir()
        {
        #ifdef _WIN32
            wchar_t appPath[512];
            if(GetModuleFileName(NULL, appPath, 512) <= 0)
            {
                return "";
            }
            else
            {
                return QDir::toNativeSeparators(QFileInfo(QString::fromWCharArray(appPath)).absolutePath());
            }
        #else
            char appProc[20];
            char appPath[1024] = { 0 };
            sprintf(appProc, "/proc/%d/exe", getpid());
            readlink(appProc, appPath, 1024);
            return QDir::toNativeSeparators(QFileInfo(appPath).absolutePath());
        #endif
        }
        /**
* @func:    FindFile
* @brief:   查找某个目录下特定类型的文件
* @author:  XXX 2020
* @param:   strFilePath:要搜索的文件夹
            strNameFilters:要搜索的文件类型（例如"*.xml"）
* @return:  符合的文件列表
*/
        QFileInfoList PLScanner::FindFile(const QString& strFilePath, const QString& strNameFilters)
        {
            QFileInfoList fileList;
            if (strFilePath.isEmpty() || strNameFilters.isEmpty())
            {
                return fileList;
            }

            QDir dir;
            QStringList filters;
            filters << strNameFilters;
            dir.setPath(strFilePath);
            dir.setNameFilters(filters);
            QDirIterator iter(dir, QDirIterator::Subdirectories);
            while (iter.hasNext())
            {
                iter.next();
                QFileInfo info = iter.fileInfo();
                if (info.isFile())
                {
                    fileList.append(info);
                }
            }
            return fileList;
        }

        void PLScanner::init()
        {
            auto settings = Core::OiSettings();

            modbusTcp_Addr = settings->value("ModbusTcp/Address", "192.168.10.1").toString();
            modbusTcp_Port = settings->value("ModbusTcp/Port", "502").toString();
            modbusTcp_Name = modbusTcp_Addr + ":" + modbusTcp_Port;

            rootDir = Core::OiGetPermanentDir(RootDir).replace("\\", "/");
            projectDir = Core::OiGetPermanentDir(ProjectDir).replace("\\", "/");
            imgDir = rootDir + "/images";
            productDefaultDir = rootDir + "/product";
            productDir = settings->value("Dir/ProductDir", productDefaultDir).toString();
            recordDir = settings->value("Dir/RecordDir", rootDir + "/record").toString();
            excelDir = settings->value("Dir/ExcelDir", rootDir + "/excel").toString();
            dataDir = settings->value("Dir/DataDir", rootDir + "/data").toString();
            correctedDataDir = settings->value("Dir/CorrectedDataDir", rootDir + "/correctData").toString();

            acquireVel = settings->value("Acquire/Vel", 100.0).toDouble();
            acquireDir = settings->value("Acquire/Dir", true).toBool();
            captureTimeout = settings->value("Acquire/CaptureTimeout", 20).toInt();
            sideLen = settings->value("Acquire/SideLen", 30.0).toDouble();
            pointMeasureInterval = settings->value("Acquire/PointMeasureInterval", 0.1).toDouble();
            zeroMethod = settings->value("Acquire/ZeroMethod", 0).toInt();
            resolution = settings->value("Acquire/Resolution", 0.1).toDouble();

            calibPos = settings->value("Calib/CalibPos", -55.0).toDouble();
            calibInterval = settings->value("Calib/CalibInterval", 1.0).toDouble();
            autoCalib = settings->value("Calib/AutoCalib", false).toBool();
            wideCalibEnable = settings->value("Calib/WideCalibEnable", true).toBool();
            saveCorrectedData = settings->value("Calib/SaveCorrectedData", false).toBool();

            QByteArray pBA = settings->value("Calib/WideCalib-P").value<QByteArray>();
            QDataStream pDS(&pBA, QIODevice::ReadOnly);
            pDS >> wideCalib_P;

            if (wideCalib_P.isEmpty())
            {
                wideCalib_P = loadCSV("WideCalib-P.csv");
            }

            measureOffsetY = settings->value("Measure/Offset", 0.0).toDouble();
            measureOffsetY_S = settings->value("Measure/Offset_S", 0.0).toDouble();
            measureOffsetX1 = settings->value("Measure/OffsetX1", 0.0).toDouble();
            measureOffsetX2 = settings->value("Measure/OffsetX2", 0.0).toDouble();
            measureOffsetX = settings->value("Measure/OffsetX", 0.0).toDouble();
            widthHide = settings->value("Measure/WidthHide", 0).toInt();
            sideCorrect = settings->value("Measure/SideCorrect", true).toBool();
            filter = settings->value("Measure/Filter", false).toBool();
            filterLen = settings->value("Measure/FilterLen", 30).toInt();

            thicknessWTDefault = settings->value("Measure/ThicknessWTDefault", 0.03).toDouble();
            thicknessETDefault = settings->value("Measure/ThicknessETDefault", 0.05).toDouble();
            widthWTDefault = settings->value("Measure/WidthWTDefault", 3.0).toDouble();
            widthETDefault = settings->value("Measure/WidthETDefault", 5.0).toDouble();
            edgeThickness = settings->value("Measure/EdgeThickness", 1.0).toDouble();
            precisionDefault = settings->value("Measure/precision", 2).toInt();
            materialSpace = settings->value("Measure/materialSpace", 300).toDouble();
            scaleView = settings->value("Measure/scaleView", 5).toDouble();
            elWidthOption = settings->value("Measure/ElWidthOption", 1).toInt();
            profileDispalyOption = settings->value("Measure/ProfileDispalyOption", 0xFFFF).toInt();

            paramMode = settings->value("Params/Mode", 2).toInt();
            switchDelayStopTime = settings->value("Params/SwitchDelayStopTime", 0).toDouble();
            switchDelayStartTime = settings->value("Params/SwitchDelayStartTime", 0).toDouble();

            measureAcqPointMethod = settings->value("Measure/MeasureAcqPointMethod", 1).toInt();
            measureAcqPointLen = settings->value("Measure/MeasureAcqPointLen", 1.0).toInt();
            localDataDir = "plscanner.db";

            if(!QDir(productDir).exists())
            {
                QDir().mkpath(productDir);
            }

            if(!QDir(dataDir).exists())
            {
                QDir().mkpath(dataDir);
            }

            if(!QDir(recordDir).exists())
            {
                QDir().mkpath(recordDir);
            }

            if(!QDir(excelDir).exists())
            {
                QDir().mkpath(excelDir);
            }

            if (!QDir(correctedDataDir).exists())
            {
                QDir().mkpath(correctedDataDir);
            }

            company = "OiSmart";
            companyLocal = "Suzhou";
            companyType = "CO., LTD.";
            softName = "PLScanner";
            softVersion = "1.0.0";
            manual = "help.pdf";
            icon = "icon.png";
            companyLogo = "companyIcon.png";
            companyBanner = "companyBanner.png";
//            themeName = settings->theme();
            themeName = settings->value("General/themes", "Aqua2007").toString();

            loadOEM();
            simulation = settings->value("Simulation", false).toBool();
            running = settings->value("Start", false).toBool();
            debugging = settings->value("Debug", false).toBool();
            
        #ifdef _DEBUG
            simulation = true;
            super = true;
            admin = true;
            debugging = true;

        #else

            simulation = true;
            super = false;
            //simulation = true;
            //super = true;
            //admin = true;
            //debugging = true;

        #endif // _DEBUG
        }

        void PLScanner::exit()
        {
            auto settings = Core::OiSettings();

            settings->setValue("ModbusTcp/Address", modbusTcp_Addr);
            settings->setValue("ModbusTcp/Port", modbusTcp_Port);

            settings->setValue("Dir/ProductDir", productDir);
            settings->setValue("Dir/CurveDir", dataDir);
            settings->setValue("Dir/ExcelDir", excelDir);
            settings->setValue("Dir/RecordDir", recordDir);
            settings->setValue("Dir/CorrectedDataDir", correctedDataDir);

            settings->setValue("Acquire/Vel", acquireVel);
            settings->setValue("Acquire/Dir", acquireDir);
            settings->setValue("Acquire/CaptureTimeout", captureTimeout);
            settings->setValue("Acquire/SideLen", sideLen);
            settings->setValue("Acquire/PointMeasureInterval", pointMeasureInterval);
            settings->setValue("Acquire/ZeroMethod", zeroMethod);
            settings->setValue("Acquire/Resolution", resolution);

            settings->setValue("Calib/CalibPos", calibPos);
            settings->setValue("Calib/CalibInterval", calibInterval);
            settings->setValue("Calib/AutoCalib",  autoCalib);
            settings->setValue("Calib/WideCalibEnable", wideCalibEnable);
            settings->setValue("Calib/SaveCorrectedData", saveCorrectedData);

//             QByteArray pBA;
//             QDataStream pDS(&pBA, QIODevice::WriteOnly);
//             pDS << wideCalib_P;
            settings->setValue("Calib/WideCalib-P", "");
            saveCSV("WideCalib-P.csv", wideCalib_P);

            settings->setValue("Measure/Offset", measureOffsetY);
            settings->setValue("Measure/Offset_S", measureOffsetY_S);
            settings->setValue("Measure/OffsetX1", measureOffsetX1);
            settings->setValue("Measure/OffsetX2", measureOffsetX2);
            settings->setValue("Measure/OffsetX", measureOffsetX);

            settings->setValue("Measure/WidthHide", widthHide);
            settings->setValue("Measure/SideCorrect", sideCorrect);
            settings->setValue("Measure/Filter", filter);
            settings->setValue("Measure/FilterLen", filterLen);
            settings->setValue("Measure/ElWidthOption", elWidthOption);

            settings->setValue("Measure/ThicknessWTDefault", thicknessWTDefault);
            settings->setValue("Measure/ThicknessETDefault", thicknessETDefault);
            settings->setValue("Measure/WidthWTDefault", widthWTDefault);
            settings->setValue("Measure/WidthETDefault", widthETDefault);
            settings->setValue("Measure/Precision", precisionDefault);
            settings->setValue("Measure/materialSpace", materialSpace);
            settings->setValue("Measure/EdgeThickness", edgeThickness);
            settings->setValue("Measure/scaleView", scaleView);
            settings->setValue("Measure/materialSpace", materialSpace);
            settings->setValue("Measure/EdgeThickness", edgeThickness);
            settings->setValue("Measure/ProfileDispalyOption", profileDispalyOption);

            settings->setValue("Params/Mode", paramMode);
            settings->setValue("Params/SwitchDelayStopTime", switchDelayStopTime);
            settings->setValue("Params/SwitchDelayStartTime", switchDelayStartTime);

            settings->setValue("Measure/MeasureAcqPointMethod", measureAcqPointMethod);
            settings->setValue("Measure/MeasureAcqPointLen", measureAcqPointLen);

            settings->sync();
        }

        void PLScanner::loadOEM()
        {
            QSettings settings(rootDir + "/pls_agent", QSettings::IniFormat);

            company = settings.value("Company", company).toString();
            companyLocal = settings.value("CompanyLocal", companyLocal).toString();
            companyType = settings.value("CompanyType", companyType).toString();
            softName = settings.value("SoftName", softName).toString();
            softVersion = settings.value("SoftVersion", softVersion).toString();
            manual = settings.value("Manual", manual).toString();
            companyLogo = settings.value("CompanyLogo", companyLogo).toString();
            icon = settings.value("Icon", icon).toString();
            companyBanner = settings.value("CompanyBanner", companyBanner).toString();
            themeName = settings.value("Theme", themeName).toString();
            resourceName = settings.value("Resource").toString();
        }

        QList<QPointF> PLScanner::loadCSV(QString fileName)
        {
            QList<QPointF> ps;

            QFile file(fileName);
            if (!file.open(QIODevice::ReadOnly))
            {
                return ps;
            }

            QTextStream in(&file);

            if (in.atEnd())
            {
                file.close();
                return ps;
            }

            QString fileLine = in.readLine();

            QStringList linelist = fileLine.split(",");

            if (linelist.size() < 2 || linelist[0] != "X" && linelist[1] != "Y")
            {
                file.close();
                return ps;
            }

            while (!in.atEnd())
            {
                fileLine = in.readLine();
                linelist = fileLine.split(",");

                if (linelist.size() >= 2)
                {
                    ps.append(QPointF(linelist[0].toDouble(), linelist[1].toDouble()));
                }

            }

            file.close();
            return ps;
        }

        void PLScanner::saveCSV(QString fileName, QList<QPointF> ps)
        {
            QFile file(fileName);
            if (!file.open(QIODevice::WriteOnly))
            {
                return;
            }

            file.write("X,Y\n");

            for (auto p : ps)
            {
                file.write(QString("%1,%2\n").arg(p.x(), 0, 'f', 2).arg(p.y(), 0, 'f', 2).toLocal8Bit());
            }

            file.close();
        }

        void PLScanner::saveCalibRecord(double temp1, double temp2,double value)
        {
            QFile file("CalibRecord.csv");
            if (!file.open(QIODevice::ReadWrite | QIODevice::Append))
            {
                return;
            }

            file.write(QString("%1,%2,%3,%4\n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(temp1, 0, 'f', 2).arg(temp2, 0, 'f', 2).arg(value, 0, 'f', 4).toLocal8Bit());

            file.close();
        }

        QString PLScanner::OiCompanyName()
        {
            QSettings settings(getAppDir().replace("\\", "/") + "/pls_agent", QSettings::IniFormat);

            return settings.value("Company", "OiSmart").toString();
        }

        QString PLScanner::OiSoftName()
        {
            QSettings settings(getAppDir().replace("\\", "/") + "/pls_agent", QSettings::IniFormat);

            return settings.value("SoftName", "PLScanner").toString();
        }

        PLScanner* PLScanner::instance()
        {
            static PLScanner s_sliceBalance; return &s_sliceBalance;
        }

        static QSettings* pLocalSettings;

        LT360X* LT360X::instance()
        {
            static LT360X lt360; return &lt360;
        }

        void LT360X::init()
        {
            if (!QDir().exists(projectDir))
            {
                QDir().mkdir(projectDir);
            }
            if (!QDir().exists(recordDir))
            {
                QDir().mkdir(recordDir);
            }

            auto* setting = Core::OiSettings();
            int type = setting->value("CaptureType", 1).toInt();
            if (type == 2)
            {
                cameraNames.swap(QList<QString>({ "OI_01", "OI_02", "OI_03", "OI_04", "OI_05", "OI_06", "OI_07", "OI_08" }));
                cameraParams.swap(QList<QString>({ "Homo1", "Homo2", "Homo3", "Homo4", "Homo5", "Homo6", "Homo7", "Homo8" }));
            }

            loadGlobalSettings();

            loadHomography();

            for (auto& name : cameraNames)
            {
                calibs[name] = Core::Calib::create(name);
            }

            QString iniFilePath = settingDir + QString("/LT360X.ini");

            pLocalSettings = new QSettings(iniFilePath, QSettings::IniFormat);

            QFileInfo fileInfo(iniFilePath);
            QDir qDir = fileInfo.absoluteDir();
            qDir.mkpath(qDir.path());

            if (!historyDir().isEmpty())
            {
                recordDir = historyDir();
                recycleDir = recordDir + "/baddata";
            }

            if (!QDir().exists(projectDir))
            {
                QDir().mkpath(projectDir);
            }
            if (!QDir().exists(recordDir))
            {
                QDir().mkpath(recordDir);
            }

            if (!QFile(iniFilePath).exists())
            {
                pLocalSettings->setValue("config/markingDelayLines", markingDelayLines());
                pLocalSettings->setValue("config/markingFrequency", markingFrequency());
                pLocalSettings->setValue("config/NGDurationThreshold", NGDurationThreshold());
                pLocalSettings->setValue("config/alarmEnabled", alarmEnabled());
                pLocalSettings->setValue("config/markingEnabled", markingEnabled());

                pLocalSettings->setValue("radius", 500);
                pLocalSettings->setValue("smoothingRadius", smoothingRadius());
                //pLocalSettings->setValue("updateChartInterval", updateChartInterval());
                pLocalSettings->setValue("roiWidth", roiWidth());
                pLocalSettings->setValue("roiHeight", roiHeight());
                pLocalSettings->setValue("resolutionY", resolutionY());
                pLocalSettings->setValue("surfaceInspectionConnectionThreshold", surfaceInspectionConnectionThreshold());
                pLocalSettings->setValue("gap", gap());
                pLocalSettings->setValue("shakeThreshold", shakeThreshold());
                pLocalSettings->setValue("profileMissThreshold", profileMissThreshold());
                pLocalSettings->setValue("activationThreshold", activationThreshold());
                pLocalSettings->setValue("surfaceInspectionContactDistance", surfaceInspectionContactDistance());
                //pLocalSettings->setValue("displayPointMapConnectionThreshold", displayPointMapConnectionThreshold());
                pLocalSettings->setValue("config/dataViewerInterval", dataViewerInterval());
                pLocalSettings->setValue("markingSignalMinGap", markingSignalMinGap());
                pLocalSettings->setValue("NoiseThreshold", noiseThreshold());
                pLocalSettings->setValue("ConnectionThreshold", connectionThreshold());
                pLocalSettings->setValue("LinkingThreshold", linkingThreshold());
                pLocalSettings->setValue("PLCIp", PLCIp());
                pLocalSettings->setValue("PLCPort", PLCPort());
                pLocalSettings->setValue("realTimeFps", realTimeFps());
                pLocalSettings->setValue("cameraSyncThreshold", cameraSyncThreshold());
                pLocalSettings->setValue("sensorTemperatureLimit", sensorTemperatureLimit());
                pLocalSettings->setValue("runSignalRegister", runSignalRegister());
                pLocalSettings->setValue("runSignalValue", runSignalValue());
                pLocalSettings->setValue("runSignalHeartbeat", runSignalHeartbeat());
                pLocalSettings->setValue("markSignalRegister", markSignalRegister());
                pLocalSettings->setValue("markSignalValue", markSignalValue());
                pLocalSettings->setValue("alarmRegister", alarmRegister());
                pLocalSettings->setValue("alarmValue", alarmValue());
                pLocalSettings->setValue("profileAlarmRegister", profileAlarmRegister());
                pLocalSettings->setValue("profileAlarmValue", profileAlarmValue());
                pLocalSettings->setValue("surfaceAlarmRegister", surfaceAlarmRegister());
                pLocalSettings->setValue("surfaceAlarmValue", surfaceAlarmValue());
                pLocalSettings->setValue("encoderCountRegister", encoderCountRegister());
                pLocalSettings->setValue("windowMode", windowMode());
                pLocalSettings->setValue("profileOnly", profileOnly());
                pLocalSettings->setValue("windowWidth", windowWidth());
                pLocalSettings->setValue("poleSize", poleSize());
                pLocalSettings->setValue("gageSize", gageSize());
                pLocalSettings->setValue("recordPerformace", recordPerformace());
                pLocalSettings->setValue("driver", driver());
                pLocalSettings->setValue("appName", appName());
                pLocalSettings->setValue("defectMarking", defectMarking());
                pLocalSettings->setValue("profileMarking", profileMarking());
                pLocalSettings->setValue("laserThreshold", laserThreshold());
                pLocalSettings->setValue("laserWidthThreshold", laserWidthThreshold());
                pLocalSettings->setValue("imageLogging", imageLogging());
                pLocalSettings->setValue("matLogging", matLogging());
                pLocalSettings->setValue("coatingDetection", coatingDetection());
                pLocalSettings->setValue("serialPort", serialPort());
                //pLocalSettings->setValue("markingSlots", markingSlots());
                pLocalSettings->setValue("historyDir", historyDir());
                pLocalSettings->setValue("defectToolNameFontSize", defectToolNameFontSize());
                pLocalSettings->setValue("defectToolCountFontSize", defectToolCountFontSize());

                this->setProfileOffsets(this->profileOffsets());
            }
        }

        void LT360X::exit()
        {
            saveGlobalSettings();
        }

        void LT360X::sync()
        {
            pLocalSettings->sync();
        }

        void LT360X::loadGlobalSettings()
        {
        }

        void LT360X::saveGlobalSettings()
        {
        }

        QString LT360X::formula() const
        {
            if (Core::Settings::instance()->contains("formula"))
            {
                return Core::Settings::instance()->value("formula").toString();
            }
            return QString();
        }

        void LT360X::setFormula(const QString formula)
        {
            Core::Settings::instance()->setValue("formula", formula);
            Core::Settings::instance()->sync();
        }

        Math::Point2d LT360X::undistort(const QString& name, const Math::Point2d& point)
        {
            auto* setting = Core::OiSettings();
            int type = setting->value("CaptureType", 1).toInt();
            if (type == 1)
            {
                auto calib = ThreadSafeMapOperator(calibs, name);
                if (calib.isNull() || calib->model() < 0) return point;
                auto undistortPoint = calib->imageToUndistortedImagePlane(point);

                return Math::Point2d(undistortPoint.x() * calib->F() / calib->Sx() + calib->Cx(), undistortPoint.y() * calib->F() / calib->Sy() + calib->Cy());
            }
            
            return LTCameraDriver::instance()->calibrationTransformSection(name, point);
        }

        void LT360X::rangeMapsToPoints(const QMap<QString, Core::RangeMapPtr>& rangeMaps, QList<QPointF>& points, bool smooth)
        {
            double radiusSq = LT360X::instance()->radiusSq();
            for (auto& cameraName : LT360X::instance()->cameraNames)
            {
                auto rangemap = ThreadSafeMapOperator(rangeMaps, cameraName);
                auto homography = ThreadSafeMapOperator(LT360X::instance()->homography, cameraName);
                auto offsetX = 0;//LTCameraDriver::instance()->offsetX(cameraName);
                auto offsetY = 0;//LTCameraDriver::instance()->offsetY(cameraName);

                if (rangemap && rangemap->rows() > 0)
                {
                    int takeProfile = 0;

                    for (int i = 0; i < rangemap->cols(); i++)
                    {
                        if (rangemap->profile(takeProfile)[i] > 0)
                        {
                            double rx = i + offsetX;
                            double ry = rangemap->profile(takeProfile)[i] + offsetY;

                            auto undistortedPoint = undistort(cameraName, Math::Point2d(rx, ry));
                            rx = undistortedPoint.x();
                            ry = undistortedPoint.y();

                            if (Math::IsNan(rx))
                            {
                                continue;
                            }

                            float denum = homography.elementAt(2, 0) * rx + homography.elementAt(2, 1) * ry + homography.elementAt(2, 2);
                            float x = (homography.elementAt(0, 0) * rx + homography.elementAt(0, 1) * ry + homography.elementAt(0, 2)) / denum;
                            float y = (homography.elementAt(1, 0) * rx + homography.elementAt(1, 1) * ry + homography.elementAt(1, 2)) / denum;
                            if (x * x + y * y < radiusSq)
                            {
                                points.append(QPointF(x, y));
                            }
                        }
                    }
                }
            }

            if (smooth)
            {
                if (points.size() < 10)return;
                double smoothingRadiusSq = LT360X::instance()->smoothingRadius() * LT360X::instance()->smoothingRadius();
                if (smoothingRadiusSq > 0)
                {
                    auto tree = Core::KDTree::create(points);
                    for (auto& point : points)
                    {
                        std::vector<size_t> results;
                        tree->radiusSearch(point.x(), point.y(), smoothingRadiusSq, results);
                        float x = 0;
                        float y = 0;
                        for (auto& index : results)
                        {
                            x = x + tree->x(index);
                            y = y + tree->y(index);
                        }

                        if (results.size() > 0)
                        {
                            x = x / float(results.size());
                            y = y / float(results.size());
                            point.rx() = x;
                            point.ry() = y;
                        }
                    }
                }
            }
        }

        int LT360X::fontSize()
        {
            return pLocalSettings->value("config/fontSize", 22).toInt();
        }

        void LT360X::setFontSize(int fontSize)
        {
            pLocalSettings->setValue("config/fontSize", fontSize);
            pLocalSettings->sync();
        }

        inline Math::Matrix44d toMatrixHelper(const cv::Mat& mat)
        {
            Math::Matrix44d result;
            result.setIdentity();
            result.elementAt(0, 0) = mat.at<double>(0, 0);
            result.elementAt(0, 1) = mat.at<double>(0, 1);
            result.elementAt(0, 2) = mat.at<double>(0, 2);
            result.elementAt(0, 3) = mat.at<double>(0, 3);
            result.elementAt(1, 0) = mat.at<double>(1, 0);
            result.elementAt(1, 1) = mat.at<double>(1, 1);
            result.elementAt(1, 2) = mat.at<double>(1, 2);
            result.elementAt(1, 3) = mat.at<double>(1, 3);
            result.elementAt(2, 0) = mat.at<double>(2, 0);
            result.elementAt(2, 1) = mat.at<double>(2, 1);
            result.elementAt(2, 2) = mat.at<double>(2, 2);
            result.elementAt(2, 3) = mat.at<double>(2, 3);

            return result;
        }

        inline Math::Matrix33d toMatrixHelper2(const cv::Mat& mat)
        {
            Math::Matrix33d result;
            if (mat.rows == 2)
            {
                result.elementAt(0, 0) = mat.at<double>(0, 0);
                result.elementAt(0, 1) = mat.at<double>(0, 1);
                result.elementAt(0, 2) = mat.at<double>(0, 2);
                result.elementAt(1, 0) = mat.at<double>(1, 0);
                result.elementAt(1, 1) = mat.at<double>(1, 1);
                result.elementAt(1, 2) = mat.at<double>(1, 2);
                result.elementAt(2, 0) = 0;
                result.elementAt(2, 1) = 0;
                result.elementAt(2, 2) = 1;
            }
            else if (mat.rows == 3)
            {
                result.elementAt(0, 0) = mat.at<double>(0, 0);
                result.elementAt(0, 1) = mat.at<double>(0, 1);
                result.elementAt(0, 2) = mat.at<double>(0, 2);
                result.elementAt(1, 0) = mat.at<double>(1, 0);
                result.elementAt(1, 1) = mat.at<double>(1, 1);
                result.elementAt(1, 2) = mat.at<double>(1, 2);
                result.elementAt(2, 0) = mat.at<double>(2, 0);
                result.elementAt(2, 1) = mat.at<double>(2, 1);
                result.elementAt(2, 2) = 1;//mat.at<double>(2, 2);
            }

            return result;
        }

        void LT360X::loadHomography()
        {
            cv::Mat homo;
            cv::FileStorage opencv_file("../Setting/NewCalibrationData.xml", cv::FileStorage::READ);

            if (!QFile::exists("../Setting/NewCalibrationData.xml"))
            {
                for (int i = 0; i < cameraNames.size(); i++)
                {
                    Math::Matrix33d identity;
                    identity.setIdentity();
                    homography[cameraNames[i]] = identity;
                }

                return;
            }
            opencv_file.open("../Setting/NewCalibrationData.xml", cv::FileStorage::READ);
            for (int i = 0; i < cameraNames.size(); i++)
            {
                int camInd = cameraNames[i].right(1).toInt() - 1;
                std::string paramId = cameraParams[camInd].toStdString();
                if (!opencv_file[paramId].isNone())
                {
                    opencv_file[paramId] >> homo;
                    Math::Matrix33d rotationMatrix = Math::Matrix33d::identity();
                    Math::Matrix33d traslationMatrix = Math::Matrix33d::identity();

                    homography[cameraNames[i]] = traslationMatrix * rotationMatrix * toMatrixHelper2(homo);
                }
                else
                {
                    homography[cameraNames[i]] = Math::Matrix33d::identity();
                }
            }
            opencv_file.release();
        }

        double LT360X::radiusSq()
        {
            double val = pLocalSettings->value("radius", 550).toDouble();
			return val * val;
        }

        double LT360X::smoothingRadius()
        {
            return pLocalSettings->value("smoothingRadius", 0.5).toDouble();
        }

        double LT360X::radius()
        {
            return pLocalSettings->value("radius", 30).toDouble();
        }

        double LT360X::updateChartInterval()
        {
            return 1000.0;
        }

        int LT360X::roiWidth()
        {
            return pLocalSettings->value("roiWidth", 1024).toInt();
            //return 1024;
        }

        int LT360X::roiHeight()
        {
            return pLocalSettings->value("roiHeight", 512).toInt();
            //return 512;
        }

        double LT360X::resolutionY()
        {
            return pLocalSettings->value("resolutionY", 0.2).toDouble();
            //return 0.2;
        }

        double LT360X::surfaceInspectionConnectionThreshold()
        {
            return pLocalSettings->value("surfaceInspectionConnectionThreshold", 0.1).toDouble();
            //return 0.1;
        }

        int LT360X::gap()
        {
            return pLocalSettings->value("gap", 50).toInt();
            //return 50;
        }

        double LT360X::shakeThreshold()
        {
            return pLocalSettings->value("shakeThreshold", 3.0).toDouble();
            //return 3.0;
        }

        double LT360X::profileMissThreshold()
        {
            return pLocalSettings->value("profileMissThreshold", 0.85).toDouble();
            //return 0.85;
        }

        double LT360X::activationThreshold()
        {
            return pLocalSettings->value("activationThreshold", 0.1).toDouble();
            //return 0.1;
        }

        double LT360X::surfaceInspectionContactDistance()
        {
            return pLocalSettings->value("surfaceInspectionContactDistance", 5.0).toDouble();
            //return 5;
        }

        double LT360X::displayPointMapConnectionThreshold()
        {
            return 2.4;
        }

        double LT360X::markingDelayLines()
        {
            return pLocalSettings->value("config/markingDelayLines", 300).toDouble();
        }

        void LT360X::setMarkingDelayLines(double lines)
        {
            pLocalSettings->setValue("config/markingDelayLines", lines);
            pLocalSettings->sync();
        }

        int LT360X::timeDelayOffset()
        {
            return pLocalSettings->value("timeDelayOffset", 300).toInt();
        }

        void LT360X::setTimeDelayOffset(int value)
        {
            pLocalSettings->setValue("timeDelayOffset", value);
            pLocalSettings->sync();
        }

        int LT360X::markingFrequency()
        {
            return pLocalSettings->value("config/markingFrequency", 60).toInt();
        }

        void LT360X::setMarkingFrequency(int distance)
        {
            pLocalSettings->setValue("config/markingFrequency", distance);
            pLocalSettings->sync();
        }

        int LT360X::NGDurationThreshold()
        {
            return pLocalSettings->value("config/NGDurationThreshold", 1).toInt();
        }

        void LT360X::setNGDurationThreshold(int threshold)
        {
            pLocalSettings->setValue("config/NGDurationThreshold", threshold);
            pLocalSettings->sync();
        }

        bool LT360X::displayModeSimple()
        {
            return pLocalSettings->value("config/displayModeSimple", true).toBool();
        }

        void LT360X::setDisplayModeSimple(bool flag)
        {
            pLocalSettings->setValue("config/displayModeSimple", flag);
            pLocalSettings->sync();
        }

        bool LT360X::alarmEnabled()
        {
            return pLocalSettings->value("config/alarmEnabled", true).toBool();
        }

        void LT360X::setAlarmEnabled(bool flag)
        {
            pLocalSettings->setValue("config/alarmEnabled", flag);
            pLocalSettings->sync();
        }

        bool LT360X::markingEnabled()
        {
            return pLocalSettings->value("config/markingEnabled", false).toBool();
        }

        void LT360X::setMarkingEnabled(bool flag)
        {
            pLocalSettings->setValue("config/markingEnabled", flag);
            pLocalSettings->sync();
        }

        int LT360X::sampleSize()
        {
            return pLocalSettings->value("config/sampleSize", 10).toInt();
        }

        void LT360X::setSampleSize(int size)
        {
            pLocalSettings->setValue("config/sampleSize", size);
            pLocalSettings->sync();
        }

        int LT360X::dataViewerInterval()
        {
            return pLocalSettings->value("config/dataViewerInterval", 30).toInt();
        }

        void LT360X::setDataViewerInterval(int interval)
        {
            pLocalSettings->setValue("config/dataViewerInterval", interval);
            pLocalSettings->sync();
        }

        double LT360X::noiseThreshold()
        {
            return pLocalSettings->value("NoiseThreshold", 5).toDouble();
        }

        void LT360X::setNoiseThreshold(double noiseThreshold)
        {
            pLocalSettings->setValue("NoiseThreshold", noiseThreshold);
            pLocalSettings->sync();
        }

        double LT360X::connectionThreshold()
        {
            return pLocalSettings->value("ConnectionThreshold", 5).toDouble();
        }

        void LT360X::setConnectionThreshold(double connectionThreshold)
        {
            pLocalSettings->setValue("ConnectionThreshold", connectionThreshold);
            pLocalSettings->sync();
        }

        double LT360X::linkingThreshold()
        {
            return pLocalSettings->value("LinkingThreshold", 8).toDouble();
        }

        void LT360X::setLinkingThreshold(double linkingThreshold)
        {
            pLocalSettings->setValue("LinkingThreshold", linkingThreshold);
            pLocalSettings->sync();
        }

        QMap<QString, QPointF> LT360X::profileOffsets()
        {
            QMap<QString, QPointF> offsets;
            for (auto name : cameraNames)
            {
                offsets[name] = QPointF(pLocalSettings->value(QString("%1_profile_offsetx").arg(name), 0).toFloat(), pLocalSettings->value(QString("%1_profile_offsety").arg(name), 0).toFloat());
            }
            return offsets;
        }

        void LT360X::setProfileOffsets(QMap<QString, QPointF> offsets)
        {
            for (auto name : cameraNames)
            {
                pLocalSettings->setValue(QString("%1_profile_offsetx").arg(name), offsets[name].x());
                pLocalSettings->setValue(QString("%1_profile_offsety").arg(name), offsets[name].y());
            }
            pLocalSettings->sync();
        }

        QString LT360X::PLCIp()
        {
            return pLocalSettings->value("PLCIp", "").toString();
        }

        QString LT360X::PLCPort()
        {
            return pLocalSettings->value("PLCPort", "502").toString();
        }

        QString LT360X::OPCServiceName()
        {
            return pLocalSettings->value("OPC/serverName", "").toString();
        }

        QString LT360X::OPCDeviceName()
        {
            return pLocalSettings->value("OPC/deviceName", "").toString();
        }

        QString LT360X::OPCRecvDataNames()
        {
            return pLocalSettings->value("OPC/recvDataNames", "").toString();
        }

        int LT360X::OPCUpdateRate()
        {
            return pLocalSettings->value("OPC/updateRate", "").toInt();
        }

        QString LT360X::OPCRecvTypes()
        {
            return pLocalSettings->value("OPC/recvTypes", "").toString();
        }

        double LT360X::speed()
        {
            return pLocalSettings->value("speed", 10.0).toDouble();
        }

        void LT360X::setSpeed(double value)
        {
            pLocalSettings->setValue("speed", value);
            pLocalSettings->sync();
        }

        int LT360X::markingSignalMinGap()
        {
            return pLocalSettings->value("markingSignalMinGap", 1000).toInt();
        }

        void LT360X::setMarkingSignalMinGap(int value)
        {
            pLocalSettings->setValue("markingSignalMinGap", value);
            pLocalSettings->sync();
        }

        QPoint LT360X::defectConfigureDialogPosition()
        {
            return QPoint(pLocalSettings->value("defectConfigureDialogPositionX", 0).toInt(), pLocalSettings->value("defectConfigureDialogPositionY", 0).toInt());
        }

        void LT360X::setDefectConfigureDialogPosition(const QPoint& point)
        {
            pLocalSettings->setValue("defectConfigureDialogPositionX", point.x());
            pLocalSettings->setValue("defectConfigureDialogPositionY", point.y());
            pLocalSettings->sync();
        }

        QPoint LT360X::measureConfigureDialogPosition()
        {
            return QPoint(pLocalSettings->value("measureConfigureDialogPositionX", 0).toInt(), pLocalSettings->value("measureConfigureDialogPositionY", 0).toInt());
        }

        void LT360X::setMeasureConfigureDialogPosition(const QPoint& point)
        {
            pLocalSettings->setValue("measureConfigureDialogPositionX", point.x());
            pLocalSettings->setValue("measureConfigureDialogPositionY", point.y());
            pLocalSettings->sync();
        }

        int LT360X::measureConfigureDialogWidth()
        {
            return pLocalSettings->value("measureConfigureDialogWidth", 400).toInt();
        }

        void LT360X::setMeasureConfigureDialogWidth(int value)
        {
            pLocalSettings->setValue("measureConfigureDialogWidth", value);
            pLocalSettings->sync();
        }

        int LT360X::realTimeFps()
        {
            return pLocalSettings->value("realTimeFps", 900).toInt();
        }

        int LT360X::markTime()
        {
            return pLocalSettings->value("markTime", 600).toInt();
        }

        int LT360X::cameraSyncThreshold()
        {
            return pLocalSettings->value("cameraSyncThreshold", 100).toInt();
        }

        double LT360X::sensorTemperatureLimit()
        {
            return pLocalSettings->value("sensorTemperatureLimit", 85).toDouble();
        }

        int LT360X::runSignalRegister()
        {
            return pLocalSettings->value("runSignalRegister", 0).toInt();
        }

        int LT360X::runSignalValue()
        {
            return pLocalSettings->value("runSignalValue", 2).toInt();
        }

        bool LT360X::runSignalHeartbeat()
        {
            return pLocalSettings->value("runSignalHeartbeat", false).toBool();
        }

        int LT360X::markSignalRegister()
        {
            return pLocalSettings->value("markSignalRegister", 1).toInt();
        }

        int LT360X::markSignalValue()
        {
            return pLocalSettings->value("markSignalValue", 1).toInt();
        }

        int LT360X::alarmRegister()
        {
            return pLocalSettings->value("alarmRegister", 0).toInt();
        }

        int LT360X::alarmValue()
        {
            return pLocalSettings->value("alarmValue", 4).toInt();
        }

        int LT360X::profileAlarmRegister()
        {
            return pLocalSettings->value("profileAlarmRegister", -1).toInt();
        }

        int LT360X::profileAlarmValue()
        {
            return pLocalSettings->value("profileAlarmValue", 1).toInt();
        }

        int LT360X::surfaceAlarmRegister()
        {
            return pLocalSettings->value("surfaceAlarmRegister", -1).toInt();
        }

        int LT360X::surfaceAlarmValue()
        {
            return pLocalSettings->value("surfaceAlarmValue", 1).toInt();
        }

        QString LT360X::windowMode()
        {
            return pLocalSettings->value("windowMode", "maximized").toString();
        }

        int LT360X::encoderCountRegister()
        {
            return pLocalSettings->value("encoderCountRegister", -1).toInt();
        }

        bool LT360X::profileOnly()
        {
            return pLocalSettings->value("profileOnly", false).toBool();
        }

        int LT360X::windowWidth()
        {
            return pLocalSettings->value("windowWidth", 1560).toInt();
        }

        double LT360X::poleSize()
        {
            return pLocalSettings->value("poleSize", 10).toDouble();
        }

        double LT360X::gageSize()
        {
            return pLocalSettings->value("gageSize", 20).toDouble();
        }

        bool LT360X::recordPerformace()
        {
            return pLocalSettings->value("recordPerformace", false).toBool();
        }

        QString LT360X::driver()
        {
            return pLocalSettings->value("driver", "Fake").toString();
        }

        QString LT360X::appName()
        {
            return pLocalSettings->value("appName", "三角胶360度轮廓检测系统-Offline").toString();
        }

        bool LT360X::defectMarking()
        {
            return pLocalSettings->value("defectMarking", true).toBool();
        }

        bool LT360X::profileMarking()
        {
            return pLocalSettings->value("profileMarking", false).toBool();
        }

        int LT360X::laserThreshold()
        {
            return pLocalSettings->value("laserThreshold", 16).toInt();
        }
        
        int LT360X::laserWidthThreshold()
        {
            return pLocalSettings->value("laserWidthThreshold", -1).toInt();
        }

        QString LT360X::language()
        {
            return pLocalSettings->value("language", "cn").toString();
        }

        void LT360X::setLanguage(const QString& language)
        {
            pLocalSettings->setValue("language", language);
            pLocalSettings->sync();
        }

        bool LT360X::imageLogging()
        {
            return imageLogFlag;
        }
        
        bool LT360X::matLogging()
        {
            return matLogFlag;
        }

        void LT360X::setImageLog(bool flag)
        {
            imageLogFlag = flag;
        }
        
        void LT360X::setMatLog(bool flag)
        {
            matLogFlag = flag;
        }

        bool LT360X::coatingDetection()
        {
            return pLocalSettings->value("coatingDetection", false).toBool();
        }

        bool LT360X::useCalib()
        {
            return pLocalSettings->value("useCalib", false).toBool();
        }

        QString LT360X::serialPort()
        {
            return pLocalSettings->value("serialPort", "").toString();
        }

        QList<QString> LT360X::markingSlots()
        {
            return QList<QString>({ "不打标", "Q0.3", "Q0.4" });
        }

        void LT360X::setRealTimeFps(int value)
        {
            pLocalSettings->setValue("realTimeFps", value);
        }

        QString LT360X::historyDir()
        {
            return pLocalSettings->value("historyDir", "").toString();
        }

        float LT360X::defectToolNameFontSize()
        {
            return pLocalSettings->value("defectToolNameFontSize", 10).toFloat();
        }

        float LT360X::defectToolCountFontSize()
        {
            return pLocalSettings->value("defectToolCountFontSize", 20).toFloat();
        }

        double LT360X::exposureTime()
        {
            return pLocalSettings->value("exposureTime", 100).toFloat();
        }

        void LT360X::setExposureTime(double v)
        {
            pLocalSettings->setValue("exposureTime", v);
        }

        double LT360X::gain()
        {
            return pLocalSettings->value("gain", 1).toFloat();
        }

        void LT360X::setGain(double v)
        {
            pLocalSettings->setValue("gain", v);
        }

        int LT360X::calibExposureTime()
        {
            return pLocalSettings->value("calibExposureTime", 50).toInt();
        }

        int LT360X::takeProfile(const Core::RangeMapPtr& rangeMap)
        {
            return rangeMap->rows() >= 5 ? 5 : 0;
        }

        int LT360X::takeProfile(const Core::PointMapPtr& pointMap)
        {
            return pointMap->rows() >= 5 ? 5 : 0;
        }

        bool LT360X::liveMode()
        {
            return pLocalSettings->value("liveMode", false).toBool();
        }

        int ThreadSafeMapOperator(const QMap<QString, int>& map, const QString& key)
        {
            return map[key];
        }

        Math::Matrix33d ThreadSafeMapOperator(const QMap<QString, Math::Matrix33d>& map, const QString& key)
        {
            return map[key];
        }
        
        Core::CalibPtr ThreadSafeMapOperator(const QMap<QString, Core::CalibPtr>& map, const QString& key)
        {
            return map[key];
        }
        
        Core::Calib3DPtr ThreadSafeMapOperator(const QMap<QString, Core::Calib3DPtr>& map, const QString& key)
        {
            return map[key];
        }

        Camera::Device* ThreadSafeMapOperator(const QMap<QString, Camera::Device*>& map, const QString& key)
        {
            return map[key];
        }

        Core::RangeMapPtr ThreadSafeMapOperator(const QMap<QString, Core::RangeMapPtr>& map, const QString& key)
        {
            return map[key];
        }

        Core::RangeMapPtrs ThreadSafeMapOperator(const QMap<QString, Core::RangeMapPtrs>& map, const QString& key)
        {
            return map[key];
        }

        QList<qint64> ThreadSafeMapOperator(const QMap<QString, QList<qint64>>& map, const QString& key)
        {
            return map[key];
        }

        //LTBase::Contours ThreadSafeMapOperator(const QMap<QString, LTBase::Contours>& map, const QString& key)
        //{
        //    return map[key];
        //}

    }
}
